﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
using Syncfusion.Pdf;
using Syncfusion.Pdf.Graphics;
using Syncfusion.Pdf.Interactive;
using Syncfusion.Windows.PdfViewer;

namespace PdfSdk.PdfViewer
{
    public class DocumentViewWrapper : ObjectWrapper
    {
        private PdfUnitConvertor m_unitConvertor = new PdfUnitConvertor();
        public readonly PdfDocumentView m_parent;
        public readonly PdfDocumentViewWrapper m_parentWrapper;
        public static readonly Type s_pdfViewerShapeType;

        static DocumentViewWrapper()
        {
            Type type = typeof(DocumentView);

            s_pdfViewerShapeType = type.GetNestedType("PdfViewerShape", BindingFlags.NonPublic);
        }

        internal DocumentViewWrapper(DocumentView documentView) : base(documentView)
        {
            m_parent = GetFieldValue(nameof(m_parent)) as PdfDocumentView;
            m_parentWrapper = m_parent.GetWrapper();
        }

        public DocumentView DocumentView => _originalObject as DocumentView;

        public ScrollViewer scrollViewer => GetFieldValue() as ScrollViewer;

        public BackgroundWorker TextSearchBackgroundworker => GetFieldValue() as BackgroundWorker;
        public int m_currentPageMatchCount => Convert.ToInt32(GetFieldValue());

        internal double CompositeBufferCount
        {
            get => Convert.ToDouble(GetFieldValue());
            set => SetFieldValue(value);
        }

        public VirtualizationListWrapper virtualizingPdfPages
        {
            get
            {
                var obj = GetFieldValue();
                return obj != null ? new VirtualizationListWrapper(obj) : null;
            }
        }

        internal ImageProviderWrapper imageProvider
        {
            get
            {
                var obj = GetFieldValue();
                if (obj != null)
                {
                    return new ImageProviderWrapper(obj);
                }
                return null;
            }
        }

        public CustomVPanel virtualPanel => GetFieldValue() as CustomVPanel;

        public INotifyCollectionChanged UndoStack => GetFieldValue() as INotifyCollectionChanged;

        public INotifyCollectionChanged RedoStack => GetFieldValue() as INotifyCollectionChanged;

        public PdfViewerShape CurrentShape
        {
            get => (PdfViewerShape)Convert.ToInt32(GetFieldValue());
            set => SetFieldValue(Enum.Parse(s_pdfViewerShapeType, value.ToString()));
        }

        internal PdfViewerFreeTextAnnotationWrapper zoomChangedFreeTextAnnotation
        {
            get
            {
                var obj = GetFieldValue();
                return obj == null ? null : new PdfViewerFreeTextAnnotationWrapper(obj);
            }
            set => SetFieldValue(value);
        }

        internal PdfViewerFreeTextAnnotationWrapper m_selectedFreeTextAnnotation
        {
            get
            {
                var obj = GetFieldValue();
                return obj == null ? null : new PdfViewerFreeTextAnnotationWrapper(obj);
            }
            set => SetFieldValue(value);
        }

        public PointF startingPoint
        {
            get => (PointF)GetFieldValue();
            set => SetFieldValue(value);
        }
        public PointF StartingPoint
        {
            get => (PointF)GetPropertyValue();
            set => SetPropertyValue(value);
        }
        public PointF EndingPoint
        {
            get => (PointF)GetFieldValue();
            set => SetFieldValue(value);
        }
        public PointF SelectedStartPoint
        {
            get => (PointF)GetFieldValue();
            set => SetFieldValue(value);
        }
        public PointF SelectedEndPoint
        {
            get => (PointF)GetFieldValue();
            set => SetFieldValue(value);
        }

        internal System.Windows.Point m_mousePosition
        {
            get => (System.Windows.Point)GetFieldValue();
            set => SetFieldValue(value);
        }

        internal Dictionary<int, StrokeCollection> m_completeInkStrokeCollections => GetFieldValue() as Dictionary<int, StrokeCollection>;

        internal System.Windows.Point PolygonDoubleTappedPoint
        {
            get => (System.Windows.Point)GetPropertyValue();
            set => SetPropertyValue(value);
        }

        public ActiveLinePoint ActivePoint => (ActiveLinePoint)Convert.ToInt32(GetFieldValue());

        public bool m_hasPDFForms
        {
            get => Convert.ToBoolean(GetFieldValue());
            set => SetFieldValue(value);
        }

        internal bool IsTouchTextSelectionEnabled
        {
            get => Convert.ToBoolean(GetFieldValue());
            set => SetFieldValue(value);
        }

        internal bool IsStampAdded
        {
            get => Convert.ToBoolean(GetFieldValue());
            set => SetFieldValue(value);
        }
        internal bool IsDragInProgress
        {
            get => Convert.ToBoolean(GetPropertyValue());
            set => SetPropertyValue(value);
        }
        public bool IsInkSelected
        {
            get => Convert.ToBoolean(GetPropertyValue());
        }

        public bool IsAnnotationHovered
        {
            get => Convert.ToBoolean(GetPropertyValue());
        }

        internal int CanvasPageIndex
        {
            get => Convert.ToInt32(GetPropertyValue());
            set => SetPropertyValue(value);
        }

        // internal Dictionary<int, Page> Pages
        public IDictionary Pages => GetPropertyValue() as IDictionary;

        public Dictionary<int, PageWrapper> PagesEx
        {
            get
            {
                if (GetPropertyValue("Pages") is IDictionary d)
                {
                    return d.Keys.Cast<int>().ToDictionary(key => key, key => new PageWrapper(d[key]));
                }
                return null;
            }
        }

        public Dictionary<int, IList> InkAnnotations
        {
            get
            {
                if (GetPropertyValue() is IDictionary d)
                {
                    return d.Keys.Cast<int>().ToDictionary(key => key, key => d[key] as IList);
                }
                return null;
            }
        }

        public Dictionary<int, IList> Shapes
        {
            get
            {
                if (GetPropertyValue() is IDictionary d)
                {
                    return d.Keys.Cast<int>().ToDictionary(key => key, key => d[key] as IList);
                }
                return null;
            }
        }

        internal Dictionary<int, IList> TextAnnotations
        {
            get
            {
                if (GetPropertyValue() is IDictionary d)
                {
                    return d.Keys.Cast<int>().ToDictionary(key => key, key => d[key] as IList);
                }
                return null;
            }
        }

        internal RedactionWrapper SelectedRegion
        {
            get
            {
                var obj = GetPropertyValue();
                if (obj != null)
                {
                    return new RedactionWrapper(obj);
                }
                return null;
            }
            set => SetPropertyValue(value);
        }
        internal RedactionWrapper MouseOveredRegion
        {
            get
            {
                var obj = GetPropertyValue();
                if (obj != null)
                {
                    return new RedactionWrapper(obj);
                }
                return null;
            }
        }

        internal AnnotationWrapper m_selectedAnnotation
        {
            get => AnnotationWrapperHelper.GetAnnotation(GetFieldValue());
            set => SetFieldValue(value);
        }

        public AnnotationWrapper SelectedAnnotation
        {
            get => AnnotationWrapperHelper.GetAnnotation(GetPropertyValue());
            set => SetPropertyValue(value);
        }

        public AnnotationWrapper CurrentAnnotations
        {
            get => AnnotationWrapperHelper.GetAnnotation(GetPropertyValue());
            set => SetPropertyValue(value);
        }

        public AnnotationWrapper GetAnnotation(string annotationName, int pageIndex, bool isFromShow, bool isFromDelete)
        {
            var obj = InvokeMethod("GetAnnotation", annotationName, pageIndex, isFromShow, isFromDelete);
            return AnnotationWrapperHelper.GetAnnotation(obj);
        }

        internal void AddAnnotationHistory(object annotation, PdfDocumentViewWrapper.PdfViewerActionType actionType, int pageIndex, object layerHistory, object redactionHistory, TransactionState state)
        {
            InvokeMethod("AddAnnotationHistory",
                annotation,
                Enum.Parse(PdfDocumentViewWrapper.s_pdfViewerActionTypeType, actionType.ToString()),
                pageIndex,
                layerHistory,
                redactionHistory,
                Enum.Parse(PdfViewerTypes.TransactionState, state.ToString()));
        }

        internal AnnotationWrapper PreviousShapeAnnotation
        {
            get => AnnotationWrapperHelper.GetAnnotation(GetPropertyValue());
            set => SetPropertyValue(value);
        }

        internal AnnotationWrapper PreviousAnnotation
        {
            get => AnnotationWrapperHelper.GetAnnotation(GetPropertyValue());
            set => SetPropertyValue(value);
        }

        public PdfViewerShapeAnnotationWrapper CurrentAnnotation
        {
            get
            {
                var obj = GetPropertyValue();
                return obj == null ? null : new PdfViewerShapeAnnotationWrapper(obj);
            }
            set => SetPropertyValue(value);
        }

        public LinkedList<System.Windows.Point> PolygonPoints => GetPropertyValue() as LinkedList<System.Windows.Point>;

        public bool IsTocOrHyperlinkHOvered => Convert.ToBoolean(GetPropertyValue());
        public bool IsShapeSelected
        {
            get => Convert.ToBoolean(GetPropertyValue());
            set => SetPropertyValue(value);
        }

        internal bool IsPolylineInProgress
        {
            get => Convert.ToBoolean(GetPropertyValue());
            set => SetPropertyValue(value);
        }
        internal bool IsAnnotationSelected
        {
            get => Convert.ToBoolean(GetPropertyValue());
            set => SetPropertyValue(value);
        }
        internal int PolygonPageIndex
        {
            get => Convert.ToInt32(GetPropertyValue());
            set => SetPropertyValue(value);
        }
        internal int PolygonStartRectangleWidth
        {
            get => Convert.ToInt32(GetPropertyValue());
            set => SetPropertyValue(value);
        }
        internal int PolylinePageIndex
        {
            get => Convert.ToInt32(GetPropertyValue());
            set => SetPropertyValue(value);
        }
        internal bool IsFreeTextSelected
        {
            get => Convert.ToBoolean(GetPropertyValue());
            set => SetPropertyValue(value);
        }

        internal PdfAnnotationPopupWrapper DraggedAnnotationPopup
        {
            get
            {
                var obj = GetPropertyValue();
                if (obj == null)
                    return null;
                return new PdfAnnotationPopupWrapper(obj);
            }
        }

        public void ClearAnnotationSelection() => InvokeMethod();
        internal void ClearStickyNoteSelection() => InvokeMethod();
        internal void ClearFreeTextSelection() => InvokeMethod();
        public void DeselectFreeTextAnnotation() => InvokeMethod();

        public bool SelectAnnotation(string annotationName, int pageIndex, bool bringIntoView)
        {
            return Convert.ToBoolean(InvokeMethod("SelectAnnotation", annotationName, pageIndex, bringIntoView));
        }

        public void ApplyShapeSelection(PdfViewerShapeAnnotationWrapper shapeAnnotation)
        {
            if (shapeAnnotation == null)
                return;

            InvokeMethod("ApplyShapeSelection", shapeAnnotation.OriginalObject);
        }

        public void DocumentView_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            var @this = sender as DocumentView;

            int pageindex;
            GetPageIndex(e.GetPosition(@this).Y, out pageindex);

            var pages = this.PagesEx;
            if (pages == null || !pages.ContainsKey(pageindex))
                return;
            PageWrapper page = pages[pageindex];
            if (page == null)
                return;
            SelectionCanvasWrapper canvasWrapper = page.CanvasEx;
            if (canvasWrapper == null)
                return;
            #region Custom
            if (canvasWrapper.InkCanvas.EditingMode == InkCanvasEditingMode.Ink)
                return;
            #endregion
            System.Windows.Point position = e.GetPosition(canvasWrapper.InkCanvas);
            if (m_unitConvertor == null)
                m_unitConvertor = new PdfUnitConvertor();
            System.Windows.Point point = new System.Windows.Point((double)m_unitConvertor.ConvertFromPixels((float)position.X, PdfGraphicsUnit.Point), (double)m_unitConvertor.ConvertFromPixels((float)position.Y, PdfGraphicsUnit.Point));
            SizeF parentSize = new SizeF(@this.LoadedDocument.Pages[pageindex].Size.Width * (float)@this.ZoomFactor, @this.LoadedDocument.Pages[pageindex].Size.Height * (float)@this.ZoomFactor);
            PointF rotatedPoint = new PointF((float)point.X, (float)point.Y);
            if (page.Rotation == 90.0)
                rotatedPoint = RemoveRotations(rotatedPoint, 90.0, parentSize);
            else if (page.Rotation == 180.0)
                rotatedPoint = RemoveRotations(rotatedPoint, 180.0, parentSize);
            else if (page.Rotation == 270.0)
                rotatedPoint = RemoveRotations(rotatedPoint, 270.0, parentSize);
            point.X = (double)rotatedPoint.X;
            point.Y = (double)rotatedPoint.Y;
            if (page.IsTextSelectionStarted)
                @this.Cursor = Cursors.IBeam;
            if (page.IsTextSelectionStarted || SelectedAnnotation != null || !CheckMarkupBounds(new PointF((float)point.X, (float)point.Y), true))
                return;
            e.Handled = true;
        }

        public bool CheckMarkupBounds(PointF temprect, bool selectAnnotation)
        {
            return Convert.ToBoolean(InvokeMethod("CheckMarkupBounds", temprect, selectAnnotation));
        }

        public void GetPageIndex(double yPosition, out int pageindex)
        {
            object[] parameters = new object[] { yPosition, null };
            InvokeMethod("GetPageIndex", parameters);
            pageindex = Convert.ToInt32(parameters[1]);
        }

        internal PointF RemoveRotations(PointF rotatedPoint, double rotatedAngle, SizeF parentSize)
        {
            if (rotatedAngle == 90.0)
                rotatedPoint = new PointF(rotatedPoint.Y, parentSize.Height - rotatedPoint.X);
            else if (rotatedAngle == 180.0)
                rotatedPoint = new PointF(parentSize.Width - rotatedPoint.X, parentSize.Height - rotatedPoint.Y);
            else if (rotatedAngle == 270.0)
                rotatedPoint = new PointF(parentSize.Width - rotatedPoint.Y, rotatedPoint.X);
            return rotatedPoint;
        }

        public System.Windows.Rect RemoveRotations(System.Windows.Rect rotatedBounds, double rotatedAngle, SizeF parentSize)
        {
            if (rotatedAngle == 90.0)
            {
                rotatedBounds.Size = new System.Windows.Size(rotatedBounds.Height, rotatedBounds.Width);
                rotatedBounds.Location = new System.Windows.Point(rotatedBounds.Y, (double)parentSize.Height - (rotatedBounds.X + rotatedBounds.Height));
            }
            else if (rotatedAngle == 180.0)
                rotatedBounds.Location = new System.Windows.Point((double)parentSize.Width - (rotatedBounds.Width + rotatedBounds.X), (double)parentSize.Height - (rotatedBounds.Height + rotatedBounds.Y));
            else if (rotatedAngle == 270.0)
            {
                rotatedBounds.Size = new System.Windows.Size(rotatedBounds.Height, rotatedBounds.Width);
                rotatedBounds.Location = new System.Windows.Point((double)parentSize.Width - (rotatedBounds.Y + rotatedBounds.Width), rotatedBounds.X);
            }
            return rotatedBounds;
        }

        internal void HideLoadingIndicator()
        {
            InvokeMethod();
        }

        internal void DisplayLoadingIndicator()
        {
            InvokeMethod();
        }

        internal void RemoveAddedStickySelection() => InvokeMethod();

        public void RefreshInkStroke(PdfViewerInkAnnotationWrapper inkAnnotation)
        {
            InvokeMethod("RefreshInkStroke", inkAnnotation.OriginalObject);
        }

        public void RefreshFreeTextAnnotation(PdfViewerFreeTextAnnotationWrapper freeTextAnnotation)
        {
            InvokeMethod("RefreshFreeTextAnnotation", freeTextAnnotation.OriginalObject);
        }

        internal void SetScrollbarEnableState(string scrollbarName, bool isEnable) => InvokeMethod("SetScrollbarEnableState", scrollbarName, isEnable);
        internal void ClearControlAnnotationSelection() => InvokeMethod();
        public void ClearSelection() => InvokeMethod();
        internal void ClearPolylineSelection() => InvokeMethod();
        internal void ClearPolygonSelection() => InvokeMethod();

        public void ClearPage(int pageIndex) => InvokeMethod("ClearPage", pageIndex);
        public void ClearPages() => InvokeMethod();

        internal void InvokeSelectEvent(AnnotationWrapper annotation)
        {
            InvokeMethod("InvokeSelectEvent", annotation?.OriginalObject);
        }

        internal void InvokeDeselectEvent()
        {
            InvokeMethod("InvokeDeselectEvent");
        }

        public object AddTextMarkupAnnotations(
            int pageIndex,
            List<System.Drawing.RectangleF> rectangles,
            PdfTextMarkupAnnotationType markupAnnotationType,
            Syncfusion.Pdf.Graphics.PdfColor markupColor,
            PdfLayer annotationLayer,
            bool readOnly,
            string markupText,
            PdfLoadedTextMarkupAnnotation annot)
        {
            return InvokeMethod(
                "AddTextMarkupAnnotations",
                pageIndex,
                rectangles,
                markupAnnotationType,
                markupColor,
                annotationLayer,
                readOnly,
                markupText,
                annot);
        }

        internal void DisableTouchTextSelection() => InvokeMethod();

        public void CreateFlattenedDocument() => InvokeMethod();

        public void DisposeFormFieldPrintDocument() => InvokeMethod();

        public List<PrintPageWrapper> m_printPages
        {
            get
            {
                List<PrintPageWrapper> printPageWrappers = new();

                var list = GetFieldValue() as IList;
                if (list != null)
                {
                    for (int i = 0; i < list.Count; i++)
                    {
                        printPageWrappers.Add(new PrintPageWrapper(list[i]));
                    }
                }

                return printPageWrappers;
            }
        }

        public enum ActiveLinePoint
        {
            LineStartPoint,
            LineEndPoint,
        }

        public enum PdfViewerShape
        {
            Line,
            Rectangle,
            Circle,
            Arrow,
            None,
        }

        public InkCanvas GetInkCanvasEx(int pageIndex)
        {
            IDictionary dictionary = Pages;
            if (dictionary == null || !dictionary.Contains(pageIndex))
                return null;

            var page = dictionary[pageIndex];
            if (page == null)
                return null;

            PageWrapper pageWrapper = new PageWrapper(page);
            return pageWrapper.Canvas;
        }

        public void ChangeCanvasModeNotClearSelectionEx()
        {
            var pages = PagesEx;

            for (int key = 0; key < pages.Count; ++key)
            {
                var canvasWrapper = pages[key].CanvasEx;
                if (canvasWrapper != null)
                {
                    InkCanvas ink = canvasWrapper.InkCanvas;

                    if (m_parentWrapper.InkMode == PdfDocumentViewWrapper.PdfViewerInkAnnotationMode.Ink)
                    {
                        ink.EditingMode = InkCanvasEditingMode.Ink;
                    }
                    else
                    {
                        if (m_parentWrapper.TextMarkupMode != PdfDocumentViewWrapper.PdfViewerTextMarkupAnnotationMode.None)
                            ink.Cursor = System.Windows.Input.Cursors.IBeam;
                        else if (m_parentWrapper.ShapeMode != PdfDocumentViewWrapper.PdfViewerShapeAnnotationMode.None)
                            ink.Cursor = System.Windows.Input.Cursors.Cross;
                        else
                            ink.Cursor = m_parent.Cursor;
                        ink.EditingMode = InkCanvasEditingMode.None;
                        canvasWrapper.m_isInkModeEnabled = false;
                    }
                }
            }
        }
    }
}
